<?php if (! defined('APPPATH')) die('No direct script access allowed');
/**
 * RENARTI IK
 * An open source application development framework for PHP 5.2.0 or newer
 *
 * @package		RtiFramework
 * @author		Nauris Dambis <nauris.dambis@renarti.com>
 * @copyright	Copyright (c) 2012, RENARTI IK.
 * @license		http://renarti.com/#license
 * @link		http://renarti.com
 * @since		Version 1.0
 */

/**
 * RtiDatabase is a fast ORM tool which handles relational data.
 *
 * You can do CRUD operations with RtiDatabase or write queries manually.
 *
 * Database table relationships must be defined in a APPPATH/config/dbmap.php
 *
 * @author Nauris Dambis <nauris.dambis@renarti.com>
 * @package RtiFramework
 * @since Version 1.0
 */
class RtiDatabase
{
	protected $_alias = '';
	
	protected $_links = array();
	
	protected $_pdo;
	
	protected $_sql_select;

	protected $_sql_from;
	
	protected $_sql_join;
	
	protected $_sql_where;
	
	protected $_sql_group_by;
	
	protected $_sql_order_by;
	
	protected $_sql_having;
	
	protected $_sql_limit;
	
	protected $_statement;
	
	protected $_sqls = array();
	
	protected $_sql_string = '';
	
	protected $_sql_args = array();
	
	protected $_sql_exec_type = '';
	
	protected $_map = array();
	
	protected $_rmap = array(); 
	
	protected $_map_relate_sqls = array();
	
	
	/**
	 * Constructor
	 *
	 * Initialize the Database class and read dbmap rules 
	 *
	 * @access	public
	 * @return	void
	 */
	public function __construct()
	{
		Rti::loadConfig(array('database', 'dbmap'));		
		$this->setMap(Rti::config('dbmap')->items());
	}

	/**
	 * get all executed SQLs 
	 *
	 * @access	public
	 * @return	array
	 */
	public function getAllSQL()
	{
		return $this->_sqls;
	}
	
	/**
	 * get last executed SQL 
	 *
	 * @access	public
	 * @return	array
	 */
	public function getLastSQL()
	{
		return $this->_sql_string;	
	}

	
	
	/**
	 * Connects to the database with the specified database connection configuration alias
	 *
	 * @access	public
	 * @param	string $alias Specified database alias
	 * @return	RtiDatabase
	 */
	public function connect($alias = 'default')
	{
		if ($this->_alias == $alias)
		{
			$this->_statement = NULL;
			$this->_pdo = $this->_links[$alias];
			return $this;
		} 
		
		$config = Rti::config('database')->get($alias);
		
		if (empty($config))
		{
			Rti::showError("Incorrect database [$alias] configuration!");
		}
		
		if (! array_key_exists($alias, $this->_links))
		{
			$driver   = $config[0];
			$host     = $config[1];
			$dbname   = $config[2];
			$username = $config[3];
			$password = $config[4];
			$ext      = (isset($config[5]))? $config[5] : array();
			
			switch ($driver)
			{
				case "mysql":
					$dsn = "mysql:host=$host;dbname=$dbname";
					break;
				case "firebird":
					$dsn = "firebird:dbname=$host:$dbname";
					break;
				default:
					$dsn = "$driver:host=$host;dbname=$dbname";
					break;					
			}
			
			try 
			{
		    	//$this->_pdo = new PDO($dsn, $username, $password, array(PDO::ATTR_PERSISTENT => TRUE, PDO::ATTR_EMULATE_PREPARES => TRUE));
		    	$this->_pdo = new PDO($dsn, $username, $password, $ext);
		    	$this->_pdo->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
		    	
		    	$this->_links[$alias] = $this->_pdo;
			} 
			catch (PDOException $e) 
			{
			    Rti::showError('Connection failed [' . $alias . ']: ' . $e->getMessage());
		    }					
		}
		
		$this->reset();
		
		return $this;		
	}
	

	/**
	 * Close a database connection
	 *
	 * @access	public
	 * @param	string $alias Specified database  alias
	 * @return	void
	 */
	public function disconnect($alias = 'default')
	{
		if (isset($this->_links[$alias]))
		{
			$this->reset();
			$this->_pdo = null;
			unset($this->_links[$alias]);
		}
		
		if ($this->_alias == $alias)
		{
			$this->_alias = '';
		}
	}

	
	/**
	 * Initiates a current database transaction 
	 * 
	 * @access	public
	 * @return	void
	 */
	public function beginTransaction()
	{
		$this->_pdo->beginTransaction();
	}
	
	/**
	 * Commits a current database transaction 
	 * 	
	 * @access	public
	 * @return	void
	 */
	public function commitTransaction()
	{
		$this->_pdo->commit();		
	}
	
	/**
	 * Rolls back a current database transaction 
	 * 	
	 * @access	public
	 * @return	void
	 */
	public function rollbackTransaction()
	{
		$this->_pdo->rollBack();		
	}
	
	
	
	/**
	 * Set new database alias and connect 
	 * 	
	 * @access	public
	 * @return	RtiDatabase
	 */
	public function setAlias($value)
	{
		if ($this->_alias == $value)
		{
			return $this;
		} 
		
		$this->connect($value);
		
		$this->_alias = $value;

		return $this;		
	}
	
	/**
	 * Reset sql fields 
	 * 	
	 * @access	public
	 * @return	void
	 */
	public function reset()
	{
		$this->_sql_exec_type = '';
		$this->_sql_string = '';
		$this->_sql_args = array();
		$this->_sql_select = array();	
		$this->_sql_from = array();		
		$this->_sql_join = array();		
		$this->_sql_where = array();		
		$this->_sql_group_by = array();		
		$this->_sql_order_by = array();		
		$this->_sql_having = array();	
		$this->_sql_limit = array();	
		$this->_statement = NULL;		
	}	

	
	
	/**
	 * Generates the SELECT portion of the query
	 *
	 * @access	public
	 * @param	mixed $value string or array
	 * @return	RtiDatabase
	 */
	public function select($value = '*')
	{
		$this->reset();
		
		$this->_sql_exec_type = 'select';
		
		
		if (is_array($value))
		{
			$this->_sql_select = array_merge($this->_sql_select, $value);
		}
		else 
		{
			$this->_sql_select[] = $value;
		}
				
		return $this;
	} 
	
	/**
	 * Generates the FROM portion of the query
	 *
	 * @access	public
	 * @param	mixed $value string or array
	 * @return	RtiDatabase
	 */
	public function from($value)
	{
		if (is_array($value))
		{
			$this->_sql_from = array_merge($this->_sql_from, $value);
		}
		else 
		{
			$this->_sql_from[] = $value;
		}
				
		return $this;
	}
	
	/**
	 * Generates the JOIN portion of the query
	 *
	 * @access	public
	 * @param	string $table Table name
	 * @param	string $on join Table name 
	 * @param	string $join JOIN type (inner|left|right)
	 * @return	RtiDatabase
	 */
	public function join($table, $on, $join = 'inner')
	{
		$this->_sql_join[] = strtoupper($join) . ' JOIN ' . $table . ' ON ' . $on;
		return $this;
	}
	
	/**
	 * Generates the FROM portion of the query
	 *
	 * @access	public
	 * @param	mixed $value string or array
	 * @return	RtiDatabase
	 */
	public function where($value)
	{
		if (is_array($value))
		{
			$this->_sql_where = array_merge($this->_sql_where, $value);
		}
		else 
		{
			$this->_sql_where[] = $value;
		}				
		return $this;
	}
	
	/**
	 * Generates the GROUP BY portion of the query
	 *
	 * @access	public
	 * @param	mixed $value string or array
	 * @return	RtiDatabase
	 */
	public function groupBy($value)
	{
		if (is_array($value))
		{
			$this->_sql_group_by = array_merge($this->_sql_group_by, $value);
		}
		else 
		{
			$this->_sql_group_by[] = $value;
		}				
		return $this;
	}
	
	/**
	 * Generates the ORDER BY portion of the query
	 *
	 * @access	public
	 * @param	mixed $value string or array
	 * @return	RtiDatabase
	 */
	public function orderBy($value)
	{
		if (is_array($value))
		{
			$this->_sql_order_by = array_merge($this->_sql_order_by, $value);
		}
		else 
		{
			$this->_sql_order_by[] = $value;
		}				
		return $this;
	}
	
	/**
	 * Generates the HAVING portion of the query
	 *
	 * @access	public
	 * @param	mixed $value string or array
	 * @return	RtiDatabase
	 */
	public function having($value)
	{
		if (is_array($value))
		{
			$this->_sql_having = array_merge($this->_sql_having, $value);
		}
		else 
		{
			$this->_sql_having[] = $value;
		}				
		return $this;
	}

	/**
	 * Generates the LIMIT portion of the query 
	 * Special for MySQL database or DB hwo support for LIMIT portion
	 *
	 * @access	public
	 * @param	mixed $value string or array
	 * @return	RtiDatabase
	 */
	public function limit($value) 
	{
		if (is_array($value))
		{
			$this->_sql_limit = array_merge($this->_sql_limit, $value);
		}
		else 
		{
			$this->_sql_limit[] = $value;
		}				
		return $this;
	}	
	
	/**
	 * Generates the SQL of query 
	 *
	 * @access	public
	 * @return	RtiDatabase
	 */
	public function get()
	{
		if ($this->_sql_exec_type == 'select')
		{
			$sql = array();
				
			// select
			if (! empty($this->_sql_select))
			{
				$sql[] = 'SELECT ' . implode(', ', $this->_sql_select);
			}
				
			// from		
			if (! empty($this->_sql_from))
			{
				$sql[] = 'FROM ' . implode(', ', $this->_sql_from);
			}
				
			// join		
			if (! empty($this->_sql_join))
			{
				$sql[] = '' . implode(' ', $this->_sql_join);
			}		
				
			// where		
			if (! empty($this->_sql_where))
			{
				$sql[] = 'WHERE ' . implode(' AND ', $this->_sql_where);
			}
				
			// group by		
			if (! empty($this->_sql_group_by))
			{
				$sql[] = 'GROUP BY ' . implode(', ', $this->_sql_group_by);
			}
				
			// order by		
			if (! empty($this->_sql_order_by))
			{
				$sql[] = 'ORDER BY ' . implode(', ', $this->_sql_order_by);
			}
			
			// having		
			if (! empty($this->_sql_having))
			{
				$sql[] = 'HAVING ' . implode(' AND ', $this->_sql_having);
			}
			
			// limit
			if (! empty($this->_sql_limit)) 
			{
				$sql[] = 'LIMIT ' . implode(', ', $this->_sql_limit);
			}
				
			// result SQL
			$this->_sql_string = implode(' ', $sql);
		}
		return $this;		
	}
	
	/**
	 * Execute query
	 * Return list of model objects
	 *
	 * @access	public
	 * @param	mixed $model string or model object
	 * @return	array
	 */
	public function rows($model = '')
	{
		$this->exec();
		
		if ($model == '')
		{
			$class_name = 'stdClass';			
		}
		else 
		{
			if (is_object($model))
			{
				$class_name = get_class($model);
			}
			else 
			{
				if ($model != 'stdClass')
				{
					Rti::loadModel($model);
				}
				
				$class_name = $model;
			}
		}
		
		return $this->_statement->fetchAll(PDO::FETCH_CLASS, $class_name);
	} 
	
	/**
	 * Execute query
	 * Return model object
	 *
	 * @access	public
	 * @param	mixed $model string or model object
	 * @return	object
	 */
	public function row($model = '')
	{
		$rows = $this->rows($model);
		return (isset($rows[0]))? $rows[0] : FALSE;	
	}


	
	/**
	 * Set direct SQL 
	 *
	 * @access	public
	 * @param	string $sql if WHERE params is defined as ? then values must by set in $args param as array
	 * @param 	array $args WHERE params values 
	 * @return	RtiDatabase
	 */
	public function query($sql, $args = array())
	{
		$this->reset();
		
		$this->_sql_exec_type = 'query';
		
		$this->_sql_string = $sql ;

		$this->_sql_args = $args;
		
		return $this;		
	}
	
	/**
	 * Prepare and execute SQL without/with params
	 *
	 * @access	public
	 * @return	RtiDatabase
	 */
	public function exec()
	{
		Rti::benchmark()->start('sql');
		
		// prepare
		$this->_statement = $this->_pdo->prepare($this->_sql_string);
		
		if (! $this->_statement)
		{
			$errors[] = array();			
			$errors[] = "Can`t prepare SQL statement:";
			$errors[] = $this->_sql_string;
			$errors[] = "Arguments:";
			$errors[] = implode(",", $this->_sql_args);
			
			Rti::showError(implode("<br/>\n", $errors));
		}

		if (! $this->_statement->execute($this->_sql_args))
		{
			$errors[] = array();			
			$errors[] = "SQL statement failed:";
			$errors[] = implode("<br/>\n", $this->_pdo->errorInfo());
			$errors[] = "SQL:";
			$errors[] = $this->_sql_string;
			$errors[] = "Arguments:";
			$errors[] = implode(",", $this->_sql_args);
			
			Rti::showError(implode("<br/>\n", $errors));
		}
		
		Rti::benchmark()->stop('sql');

		$nr = count($this->_sqls);
		
		$diff = Rti::benchmark()->get('sql');
		
		// cache SQL
		$this->_sqls["$nr|$diff"] = $this->_sql_string;	
		
		return $this;
	}


	
	/**
	 * Returns the number of rows affected by the last SQL statement 
	 *
	 * @access	public
	 * @return	int
	 */
	public function affectedCount()
	{
		return $this->_statement->rowCount();
	}
	
	
	/**
	 * Returns the ID of the last inserted row or sequence value  
	 *
	 * @access	public
	 * @return	int
	 */
	public function insertId()
	{
		return $this->_pdo->lastInsertId();		
	}
	
	
	
	/**
	 * Insert new record. (Prepares and execute the INSERT statement)  
	 *
	 * @access	public
	 * @param	object $model Model object
	 * @param	array $exclude exclude properties list. For example: array('id')
	 * @return	RtiDatabase
	 */
	public function insert($model, $exclude = array())
	{
		$sql_fields = array();
		foreach ($model as $k => $v)
		{
			if (in_array($k, $exclude)) continue;
			
			$sql_fields[] = $k;
		}		
		
		$sql_values = array();
		foreach ($model as $k => $v)
		{
			if (in_array($k, $exclude)) continue;
			
			if (is_null($v))
			{
				$sql_values[] = 'NULL';
			}
			else if (is_numeric($v))
			{
				$sql_values[] = $v;
			}
			else 
			{
				$sql_values[] = "'$v'";
			}
		}
		
		$sql_insert = array();
		$sql_insert[] = 'INSERT INTO';
		$sql_insert[] = $model->getTableName();
		$sql_insert[] = "(" . implode(',', $sql_fields) . ")";
		$sql_insert[] = "VALUES";
		$sql_insert[] = "(" . implode(',', $sql_values) . ")";
		
		$sql = implode(' ', $sql_insert);
		
		return $this->query($sql)->exec();
	}
	
	/**
	 * Update an existing record. (Prepares and execute the UPDATE statement)  
	 *
	 * @access	public
	 * @param	object $model Model object
	 * @param	array $where WHERE properties list. For example: array('id')
	 * @return	RtiDatabase
	 */
	public function update($model, $where = array())
	{
		$sql_values = array();
		
		$sql_where = array();
		
		foreach ($model as $k => $v)
		{
			if (in_array($k, $where))
			{
				if (is_null($v))
				{
					$sql_where[] = "$k=NULL";
				}
				else if (is_numeric($v))
				{
					$sql_where[] = "$k=$v";
				}
				else 
				{
					$sql_where[] = "$k='$v'";
				}			
			}
			else 
			{
				if (is_null($v))
				{
					$sql_values[] = "$k=NULL";
				}
				else if (is_numeric($v))
				{
					$sql_values[] = "$k=$v";
				}
				else 
				{
					$sql_values[] = "$k='$v'";
				}
			}			
		}
		
		$sql_update = array();
		$sql_update[] = 'UPDATE';
		$sql_update[] = $model->getTableName();
		$sql_update[] = "SET";
		$sql_update[] = implode(',', $sql_values);
		
		if (! empty($sql_where))
		{
			$sql_update[] = "WHERE";
			$sql_update[] = implode(' AND ', $sql_where);
		}
		
		$sql = implode(' ', $sql_update);
		
		return $this->query($sql)->exec();
	}
	
	/**
	 * Delete an existing record. (Prepares and execute the DELETE statement)  
	 *
	 * @access	public
	 * @param	object $model Model object
	 * @param	array $where WHERE properties list. For example: array('id')
	 * @return	RtiDatabase
	 */
	public function delete($model, $where = array())
	{
		$sql_where = array();
		
		foreach ($model as $k => $v)
		{
			if (in_array($k, $where))
			{
				if (is_null($v))
				{
					$sql_values[] = "$k=NULL";
				}
				else if (is_numeric($v))
				{
					$sql_where[] = "$k=$v";
				}
				else
				{
					$sql_where[] = "$k='$v'";
				}	
			}		
		}
		
		$sql_delete = array();
		$sql_delete[] = 'DELETE FROM';
		$sql_delete[] = $model->getTableName();

		if (! empty($sql_where))
		{
			$sql_delete[] = "WHERE";
			$sql_delete[] = implode(' AND ', $sql_where);
		}
		
		$sql = implode(' ', $sql_delete);
		
		return $this->query($sql)->exec();
	}
	
	
    /**
     * Find a records. (Prepares and execute the SELECT statement)
     * @param mixed $model The model class name or object to be select
     * @param array $args WHERE properties list. For example: array('id'=>123)
     * @return array List of model objects
     */
	public function find($model, $args = array())
	{
		if (! is_object($model))
		{
			Rti::loadModel($model);
			
			$class = basename($model);
			
			$model = new $class();
		}

		$fields = $model->getFields();		
		$this->select($fields)->from($model->getTableName());	

		foreach ($fields as $field)
		{
			if (! empty($model->$field))
			{
				if (is_numeric($model->$field))
				{
					$this->where("$field={$model->$field}");
				}
				else 
				{
					$this->where("$field='{$model->$field}'");
				}
			}	
		}
		
		if (isset($args['where']))
		{
			$this->where($args['where']);
		}
		
		if (isset($args['group_by']))
		{
			$this->groupBy($args['group_by']);
		}

		if (isset($args['having']))
		{
			$this->having($args['having']);
		}
		
		if (isset($args['order_by']))
		{
			$this->orderBy($args['order_by']);
		}

		return $this->get()->rows($model);		
	}
	
    /**
     * Find a record. (Prepares and execute the SELECT statement)
     * @param mixed $model The model class name or object to be select
     * @param array $args WHERE properties list. For example: array('id'=>123)
     * @return object Model object
     */
	public function findFirst($model, $args = array())
	{
		$result = $this->find($model, $args);
		return (! empty($result))? $result[0] : FALSE;
	}
	
	public function count($model, $args = array())
	{
		if (! is_object($model))
		{
			Rti::loadModel($model);
			
			$class = basename($model);
			
			$model = new $class();
		}

		$fields = $model->getFields();		
		$this->select('COUNT(*) as TOTALCOUNT')->from($model->getTableName());	

		foreach ($fields as $field)
		{
			if (! empty($model->$field))
			{
				if (is_numeric($model->$field))
				{
					$this->where("$field={$model->$field}");
				}
				else 
				{
					$this->where("$field='{$model->$field}'");
				}
			}	
		}
		
		if (isset($args['where']))
		{
			$this->where($args['where']);
		}
		
		$row = $this->get()->row($model);

		return ($row->TOTALCOUNT);		
	}
	
	
	
	public function setMap($value)
	{
		$this->_map = $value;
		
		$this->_rmap = array();
		$this->_map_relate_sqls = array();
		
		$this->_remap();
	}
	
	public function getMap()
	{
		return $this->_map;
	}
	
	private function _remap()
	{
		// model table
		foreach ($this->_map as $model => $relationships)
		{
			// relationship
			foreach ($relationships as $relationship => $relates)
			{
				// relate table
				foreach ($relates as $relate => $joins)
				{
					// join
					foreach ($joins as $join => $value)
					{
						if ($join == 'throw')
						{
							// throw table
							foreach ($value as $throw_table => $throw_joins)
							{
								// throw join
								foreach ($throw_joins as $throw_join => $model_keys)
								{
									// model field
									foreach ($model_keys as $model_key => $relate_keys)
									{
										// throw table field 
										foreach ($relate_keys as $relate_key => $bind_property)
										{
											$this->_rmap[$model][$relate][$throw_table][$model_key][$relate_key][$throw_join][$relationship] = $bind_property;
										}
									}
								}
							}
						}
						else 
						{
							// model field
							foreach ($value as $model_key => $relate_keys)
							{
								// relate table field
								foreach ($relate_keys as $relate_key => $bind_property)
								{
									$this->_rmap[$model][$relate][$model][$model_key][$relate_key][$join][$relationship] = $bind_property;
								}
							}
						}
					}
				}
			}
		}
	}
	
	protected function _get_model_relate_sqls($model, $relate)
	{
		if (isset($this->_map_relate_sqls[$model][$relate]))
		{
			return $this->_map_relate_sqls[$model][$relate];
		}
		
		if (isset($this->_rmap[$model][$relate]))
		{
			// throw table
			foreach ($this->_rmap[$model][$relate] as $throw_table => $model_keys)
			{
				// throw table rules
				$throw = array();
				if ($model != $throw_table)
				{
					if (isset($this->_rmap[$relate][$model][$throw_table]))
					{
						$throw = $this->_rmap[$relate][$model][$throw_table];
					}
					else 
					{
						die ("Map reverse rules are not defined for [$relate] and [$model] throw [$throw_table]");
					}
				}
					
				Rti::loadModel(array($model, $relate, $throw_table));
				
				$model = new $model();
				$relate = new $relate();
				$throw_table = new $throw_table();
					
				// model field
				foreach ($model_keys as $model_key => $relates_keys)
				{
					// relate field
					foreach ($relates_keys as $relates_key => $joins)
					{
						// join
						foreach ($joins as $join => $relationships)
						{
							// $relationship
							foreach ($relationships as $relationship => $bind_property)
							{
								$sql = array();
								
								$sql[] = "SELECT r.* FROM " . $relate->getTableName() . " AS r " . strtoupper($join);	

								if (empty($throw))	
								{
									$sql[] = $model->getTableName() . " AS m ON (m." . $model_key . " = r." . $relates_key . ")"; 									
								}	
								else 
								{
									$sql[] = $throw_table->getTableName() . " AS t ON (";
									
									foreach ($throw as $r_model_key => $r_throw_keys)
									{
										foreach ($r_throw_keys as $r_throw_key => $throw_joins)
										{
											$sql[] = "r." . $r_model_key . " = t." . $r_throw_key;
										}
									}
									
									$sql[] = ")";
									$sql[] = strtoupper($join);	
									$sql[] = $model->getTableName() . " AS m ON (m." . $model_key . " =  t." . $relates_key . ")"; 									
								}		

								$this->_map_relate_sqls[get_class($model)][get_class($relate)][$relationship][$bind_property] =  implode(" ", $sql);									
							}
						}
					}
				}
			}
		}
		else 
		{
			die ("Map rules are not defined for [" . get_class($model) . " ] and [$relate]");
		}

		return $this->_map_relate_sqls[get_class($model)][get_class($relate)];
	}
	
	public function relate($model, $relates = array(), $args = array())
	{
		// model class name
		if (is_object($model))
		{
			$model_class = get_class($model);
		}
		else 
		{
			$model_class = $model;
			Rti::loadModel($model_class);
			$model = new $model_class(); 
		}
		
		
		// model objects
		$models = $this->find($model, $args);
		
		
		$model_pk = $model->getPK();
		if (empty($model_pk))
		{
			$model_pk = $model->getFields();
		}
		
		// each mmodel object
		foreach ($models as $object)
		{
			$object_fields = array();
			foreach ($object as $k=>$v)
			{
				if (in_array($k, $model_pk))
				{
					if (is_null($v))
					{
						$object_fields[] = "m.$k IS NULL";
					}
					else if (is_numeric($v))
					{
						$object_fields[] = "m.$k=$v";
					}
					else
					{
						$object_fields[] = "m.$k='$v'";
					}
				}
			}
			
			// relate
			foreach ($relates as $relate)
			{
				// get sqls for each bind property
				$map = $this->_get_model_relate_sqls($model_class, $relate);
				
				// bind relationship
				foreach ($map as $relationship => $bind_properties)
				{
					// bind property
					foreach ($bind_properties as $bind_property => $join_sql)
					{
						$sql_assoc = array();
						$sql_assoc[] = $join_sql;
						$sql_assoc[] = 'WHERE';
						$sql_assoc[] = implode(' AND ', $object_fields); 
						
						$sql = implode(' ', $sql_assoc);
						
						if ($relationship == 'has_one')
						{
							$object->$bind_property = $this->query($sql)->get()->row($relate);
						}
						else 
						{
							$object->$bind_property = $this->query($sql)->get()->rows($relate);
						}
					}					
				}
			}
		}
		return $models;
	}
	
	public function relateOne($model, $relates = array(), $args = array())
	{
		$rows = $this->relate($model, $relates, $args);
		return (! empty($rows))? $rows[0] : FALSE;
	}

	
	
}

/* End of file RtiDatabase.php */
/* Location: ./system/core/RtiDatabase.php */
